package com.zthz.douxing

import grails.util.Environment
import org.springframework.security.authentication.AuthenticationTrustResolver
import org.springframework.security.authentication.AuthenticationTrustResolverImpl
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContext
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.context.SecurityContextImpl
import org.springframework.security.web.context.HttpRequestResponseHolder
import org.springframework.security.web.context.SecurityContextRepository
import redis.clients.jedis.Jedis

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession

class RedisSecurityContextRepositoryService implements SecurityContextRepository {

    public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";

    private final Object contextObject = SecurityContextHolder.createEmptyContext();
    private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

    long expiredTime = 60 * 60
    def redisService
    def gsonBuilder

    @Override
    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        HttpServletRequest request = requestResponseHolder.getRequest();
        HttpServletResponse response = requestResponseHolder.getResponse();
        HttpSession httpSession = request.getSession(false);

        SecurityContext context = readSecurityContextFromRedis(httpSession);

        if (context == null) {
            if (log.isDebugEnabled()) {
                log.debug("No SecurityContext was available from the HttpSession: " + httpSession +". " +
                        "A new one will be created.");
            }
            context = SecurityContextHolder.createEmptyContext()
        }
        saveContext(context, request, response)
        return context
    }

    SecurityContext readSecurityContextFromRedis(HttpSession httpSession) {
        final boolean debug = log.isDebugEnabled();
        if (httpSession == null) {
            if (debug) {
                log.debug("No HttpSession currently exists");
            }
            return null;
        }

        def sessionKey = getUserKey(httpSession.getId())
        String jsonSecurityContext = redisService."${sessionKey}"
        if(debug){
            log.debug("current context is: ${jsonSecurityContext}")
        }
        def json = gsonBuilder.create()
        SecurityContext securityContext = json.fromJson(jsonSecurityContext, SecurityContextImpl);
        return securityContext
    }

    @Override
    public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
        final Authentication authentication = context.getAuthentication();
        //不管无论如何都创建session
        HttpSession httpSession = request.getSession(true);
        // See SEC-776
        if (authentication == null || authenticationTrustResolver.isAnonymous(authentication)) {
            if (log.isDebugEnabled()) {
                log.debug("SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.");
            }

            if (httpSession != null && !contextObject.equals(context)) {
                // SEC-1587 A non-anonymous context may still be in the session
                // SEC-1735 remove if the contextBeforeExecution was not anonymous
                //httpSession.removeAttribute(springSecurityContextKey);
                redisService.withRedis { Jedis jedis ->
                    jedis.del(getUserKey(httpSession.getId()))
                }
            }
            return;
        }

        def json = gsonBuilder.create()
        String contextJson = json.toJson(context);
        if(log.isDebugEnabled()){
            log.debug("SecurityContext stored to RedisSecurityContextRepositoryService: " + contextJson)
        }
        def sessionKey = getUserKey(httpSession.getId())
        redisService."${sessionKey}" = contextJson
    }

    @Override
    boolean containsContext(HttpServletRequest request) {

        def session = request.getSession(false)
        if (session == null) {
            return false;
        }
        def sessionKey = getUserKey(session.getId())
        return redisService."${sessionKey}" != null
    }

    String getUserKey(String sessionId) {
        return Environment.getCurrent().getName() + SPRING_SECURITY_CONTEXT_KEY + sessionId
    }
}
